using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

using SlimDX;
using SlimDX.Direct3D9;

namespace miciv.Rendering
{
    public class VertexBufferManager<T> : IManagedResource where T : struct
    {
        protected CoreContext m_coreContext;
        protected VertexBuffer m_vertexBuffer;
        protected int m_sVertexCount;
        protected List<ManagedVertexBuffer<T>> m_lstManagedBuffers;
        protected bool m_bIsDirty;
        protected BlocAllocationManager m_allocManager;
        protected int m_sTSize;
        protected bool m_bForceUpdate;

        public VertexBuffer VertexBuffer
        {
            get
            {
                return this.m_vertexBuffer;
            }
        }

        public bool IsDirty
        {
            get
            {
                return this.m_bIsDirty;
            }
            set
            {
                this.m_bIsDirty = value;
            }
        }

        public VertexBufferManager()
        {
            this.m_lstManagedBuffers = new List<ManagedVertexBuffer<T>>();
            this.m_allocManager = new BlocAllocationManager();
            this.m_sTSize = Marshal.SizeOf(typeof(T));
        }

        public bool Initialize(CoreContext _coreContext, int _sVertexCount)
        {
            this.m_coreContext = _coreContext;
            this.m_coreContext.GetData<ManagedResourceManager>("ManagedResourceManager").Add(this);
            this.m_sVertexCount = _sVertexCount;
            this.m_allocManager.Initialize(_sVertexCount);
            this.m_bForceUpdate = false;
            this.DeviceReset();
            return true;
        }

        public ManagedVertexBuffer<T> Create(int _sVertexCount)
        {
            if (false != this.m_allocManager.CanAlloc(_sVertexCount))
            {
                ManagedVertexBuffer<T> mVB = new ManagedVertexBuffer<T>(this);
                mVB.Offset = this.m_allocManager.Alloc(_sVertexCount);
                mVB.VertexCount = _sVertexCount;
                this.m_lstManagedBuffers.Add(mVB);
                return mVB;
            }
            else if (false != this.m_allocManager.CanRealloc(_sVertexCount))
            {
                this.m_allocManager.FreeAll();
                foreach (ManagedVertexBuffer<T> mVB in this.m_lstManagedBuffers)
                {
                    mVB.Offset = this.m_allocManager.Alloc(mVB.VertexCount);
                }
                {
                    ManagedVertexBuffer<T> mVB = new ManagedVertexBuffer<T>(this);
                    mVB.Offset = this.m_allocManager.Alloc(_sVertexCount);
                    mVB.VertexCount = _sVertexCount;
                    this.m_lstManagedBuffers.Add(mVB);
                    return mVB;
                }
            }
            return null;
        }

        public void Release(ManagedVertexBuffer<T> _mVB)
        {
            if (false != this.m_lstManagedBuffers.Contains(_mVB))
            {
                this.m_lstManagedBuffers.Remove(_mVB);
                this.m_allocManager.Free(_mVB.Offset);
            }
        }

        public void Update()
        {
            if (false != this.m_bIsDirty)
            {
                DataStream stream = this.m_vertexBuffer.Lock(0, 0, LockFlags.None);
                foreach (ManagedVertexBuffer<T> mVB in this.m_lstManagedBuffers)
                {
                    if ((false != mVB.IsDirty) || (false != this.m_bForceUpdate))
                    {
                        stream.Position = mVB.Offset * this.m_sTSize;
                        stream.WriteRange<T>(mVB.Vertices);
                        mVB.IsDirty = false;
                    }
                }
                this.m_vertexBuffer.Unlock();
                this.m_bIsDirty = false;
                this.m_bForceUpdate = false;
            }
        }

        #region IManagedResource Membres

        public void DeviceLost()
        {
            this.m_vertexBuffer.Dispose();
            this.m_vertexBuffer = null;
            this.m_bIsDirty = true;
            this.m_bForceUpdate = true;
        }

        public void DeviceReset()
        {
            this.m_vertexBuffer = new VertexBuffer(
                this.m_coreContext.GetData<SlimDX.Direct3D9.Device>("D3DDevice"),
                this.m_sVertexCount * this.m_sTSize,
                Usage.WriteOnly,
                SlimDX.Direct3D9.VertexFormat.None,
                Pool.Managed);
        }

        #endregion
    }
}
